package tr.gen._3bit.java._2k1s.api;

import gnu.io.NoSuchPortException;
import gnu.io.PortInUseException;
import gnu.io.UnsupportedCommOperationException;

import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import tr.gen._3bit.java._2k1s.api.exception.DeviceIsBusyException;
import tr.gen._3bit.java._2k1s.api.exception.NotASerialPortException;
import tr.gen._3bit.java._2k1s.api.exception.QueryCorruptedDataException;
import tr.gen._3bit.java._2k1s.api.exception.QueryTimeoutException;
import tr.gen._3bit.java._2k1s.api.exception.UnknownErrorException;
import tr.gen._3bit.java._2k1s.dao.model.SerialInterface;

public class _2K1SComm {
	private SerialComm serialComm;
	/*
	 * A delay is required to charge regulation capacitance and this varies
	 * according to capacitance of capacitor. Big values are required to work
	 * properly after long intervals.
	 */
	private Long initDelay = new Long(10000);
	private Short divisor = 16;
	private final static Pattern numericPattern = Pattern.compile("[^0-9]");
	private SerialInterface serialInterface;
	private Boolean connected = false;
	
	public _2K1SComm(SerialInterface serialInterface) {
		super();
		serialComm = new SerialComm();
		this.serialInterface = serialInterface;
	}
	public Boolean isConnected() {
		return connected;
	}

	public void connect() throws NoSuchPortException, PortInUseException,
			UnsupportedCommOperationException, IOException,
			UnknownErrorException, NotASerialPortException {
		serialComm.connect(serialInterface);
		try {
			Thread.sleep(initDelay);
		} catch (InterruptedException e1) {
			e1.printStackTrace();
		}
		try {
			String _buff = new String(serialComm.read());
			if (!_buff.contains("ent"))
				throw new UnknownErrorException("Corrupted init code");
		} catch (DeviceIsBusyException e) {
			throw new UnknownErrorException("Fatal: unknown error", e);
		}
		connected = true;
	}

	public void disconnect() throws IOException {
		serialComm.close();
		connected = false;
	}

	public synchronized Double queryTemperature(String node_id)
			throws IOException, DeviceIsBusyException,
			QueryCorruptedDataException, QueryTimeoutException {
		Double result = -500.0;
		try {
			Thread.sleep(50); // 50ms protection delay

			serialComm.write(node_id);

			Thread.sleep(100); // 100ms protection delay

			String _buff1 = new String(serialComm.read());

			if (!_buff1.contains(node_id)) {
				/*
				 * Clean up buffers.
				 */
				Thread.sleep(3000);
				serialComm.read();
				/*
				 * Throw exception.
				 */
				throw new QueryCorruptedDataException(
						"Node validation phase 1 for node: " + node_id
								+ " failed");
			}

			// 1000ms device delay + 500ms protection delay
			Thread.sleep(1500);

			String _buff2 = new String(serialComm.read());
			if (!_buff2.contains("Client " + node_id + " Temp:")) {
				Thread.sleep(250); // protection delay
				throw new QueryCorruptedDataException(
						"Node validation phase 2 for node: " + node_id
								+ " failed");
			}
			if (_buff2.contains("TIMEOUT")) {
				Thread.sleep(250); // protection delay
				throw new QueryTimeoutException("Query for node: " + node_id
						+ " timed out");
			}
			_buff2 = _buff2.split(node_id)[1];
			Matcher matcher = numericPattern.matcher(_buff2);
			Double _raw = -500.0;
			try {
				_raw = Double.parseDouble(_buff2 = matcher.replaceAll(""));
			} catch (NumberFormatException e) {
				throw new QueryCorruptedDataException("String : '" + _buff2
						+ "': " + e.getMessage(), e);
			}
			result = _raw / new Double(divisor);

			Thread.sleep(250); // protection delay

		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		return result;
	}

	public Long getInitDelay() {
		return initDelay;
	}

	/*
	 * Method to set initialization delay. Default 10s.
	 */
	public void setInitDelay(Long initDelay) {
		this.initDelay = initDelay;
	}

	public Short getDivisor() {
		return divisor;
	}

	public void setDivisor(Short divisor) {
		this.divisor = divisor;
	}
	
}
